package figure8;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;

import javax.sound.sampled.BooleanControl;

import figure8.GUI.TrackPanel;
import figure8.Network.Client;
import figure8.Network.Message;
import figure8.Network.NetworkConnection;
import figure8.events.PlaybackListener;
import figure8.events.TrackChangeListener;
import figure8.events.TrackChangedEvent;
import figure8.events.TrackEventListener;
import figure8.events.TrackRemovedEvent;

public class BaseTrack implements Track {
	private static int nextid = 1;
	
	protected int id;
	protected String name;
	protected boolean muted;
	protected boolean soloing;
	protected float volume;
	protected float pan;
	
	protected File file;
	
	protected List<TrackChangeListener> listeners;
	
	protected int size = 0;
	
	public static Track fromString(String serialized) {
		return null;
	}
	
	public Type getType() {
		return Type.NONE;
	}

	public int getID() {
		return this.id;
	}
	
	public BaseTrack(String name, boolean muted, boolean soloing, float volume, float pan) {
		this.listeners = new ArrayList<TrackChangeListener>();
		
		this.id = nextid;
		nextid++;
		
		this.name = name;
		this.muted = muted;
		this.soloing = soloing;
		this.volume = volume;
		this.pan = pan;
	}
	
	@Override
	public String getName() {
		return this.name;
	}

	@Override
	public void setName(String newName) {
		this.name = newName;
	}

	@Override
	public boolean getMuted() {
		return this.muted;
	}

	@Override
	public void setMuted(boolean muted) {
		this.muted = muted;
	}

	@Override
	public boolean getSoloing() {
		return this.soloing;
	}

	@Override
	public void setSoloing(boolean soloing) {
		this.soloing = soloing;
	}
	
	@Override
	public float getVolume() {
		return this.volume;
	}
	
	@Override
	public float getPan() {
		return this.pan;
	}

	@Override
	public void setVolume(float volume) {
		this.volume = volume;
	}
	
	@Override
	public void setPan(float pan) {
		this.pan = pan;
	}

	@Override
	public void play() {
		System.err.println("Playing not implemented for this Track type.");
	}

	@Override
	public void pause() {
		System.err.println("Pausing not implemented for this Track type.");
	}

	@Override
	public void stop() {
		System.err.println("Stopping not implemented for this Track type.");
	}

	@Override
	public TrackPanel makeTrackPanel(Controller controller) {
		System.err
				.println("Making TrackPanel not implemented for this Track type.");
		
		return null;
	}
	
	public void close () {
		
	}

	@Override
	public File getFile() {
		return this.file;
	}

	@Override
	public Boolean[][] getDrumMachine() {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public void send(NetworkConnection client, Client exclude) {
		String data = this.serializeMetadata();
		
		data = this.getType().toString() + " " + data;
		
		Message message = new Message(Message.Type.TRACK_ADD, data);
		
		client.send(message);
	}
	
	@Override
	public void sendFile(final NetworkConnection client, final Client exclude) {
		final Timer timer = new Timer();
		
		try {
			final FileInputStream input = new FileInputStream(this.file);
			final String name = this.name;
			final Track track = this;
			
			System.out.println(this.file + " " + this.file.length());
			
			final long length = this.file.length();
			
			timer.scheduleAtFixedRate(new TimerTask() {
				@Override
				public void run() {
					byte bytes[] = new byte[32 * 1024];
					byte data[];
					
					int result;
					try {
						result = input.read(bytes);
						System.out.println(result + " bytes read");
					} catch (IOException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
						System.err.println("Could not read " + name + " to send over network.");
						return;
					}
					
					if (result != -1) {
						data = new byte[result];
						
						for (int i = 0; i < data.length; ++i) {
							data[i] = bytes[i];
						}
						
						System.out.println("Read " + result + " bytes, sending " + (new String(data)).length() + " bytes.");
						
						Message message = new Message(Message.Type.TRACK_PIECE, track.getID() + " " + new String(data));
						client.send(message, exclude);
					} else {
						System.out.println("Sending done.");
						this.cancel();
					}
				}
			}, 0, 500);	
		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			System.err.println("Could not open " + this.name + " to send over network.");
		}
	}
	
	@Override
	public void filePart(String data) {
		try {			
			FileOutputStream output = new FileOutputStream(this.file, true);
			
			output.write(data.getBytes());
			output.flush();
			
			//System.out.println(data.length() + " " + this.file.length());
		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			System.err.println("Could not write received file for " + this.getName());
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			System.err.println("Could not write received file for " + this.getName());
		}
	}
	
	@Override
	public void sendMeta(NetworkConnection client) {
		String data = this.serializeMetadata();
		
		data = this.getType().toString() + " " + data;
		
		Message message = new Message(Message.Type.TRACK_UPDATE, data);
		
		client.send(message);
	}
	
	@Override
	public String serializeMetadata() {
		String name = this.name.replace(' ', '_');
		
		return Integer.toString(this.id) + " " + name + " " + Boolean.toString(muted) + " " + Float.toString(volume);
	}
	
	@Override
	public String deserializeMetadata(String metadata) {
		String[] parts = metadata.split(" ", 5);
		
		this.id = Integer.parseInt(parts[0]);
		this.name = parts[1].replace('_', ' ');
		this.muted = Boolean.parseBoolean(parts[2]);
		this.setVolume(Float.parseFloat(parts[3]));
		
		String leftover;
		
		if (parts.length < 5) {
			leftover = "";
		} else {
			leftover = parts[4];
		}
		
		return leftover;
	}
	
	public boolean ready() {
		System.out.println("Remaining bytes " + (this.size - this.file.length()));
		return (this.file.length() >= this.size);
	}

	@Override
	public void addChangeListener(TrackChangeListener listener) {
		this.listeners.add(listener);
	}

	@Override
	public void removeChangeListener(TrackChangeListener listener) {
		this.listeners.remove(listener);
	}
	
	public void fireTrackChangedEvent(boolean local) {
		for (TrackChangeListener listener : this.listeners) {
			listener.trackChanged(new TrackChangedEvent(this, this, local));
		}
	}

	@Override
	public void updateMeta(String data) {
		
	}
	
	public float percentage() {
		if (this.size > 0) {
			return ((float) this.file.length()) / this.size;
		} else {
			return -1;
		}
	}

	@Override
	public boolean isDrum() {
		// TODO Auto-generated method stub
		return false;
	}
	
	@Override
	public void finalize() {
	}
}
